#pragma once

#include <map>
#include <string>

#include <jsoncpp/json/json.h>

#include "account.hpp"

class User_info;

struct Device_Info
{
public:
    enum MODIFY
    {
        NONE = User_Info::MODIFY::NUM,
        MODIFY_BATTERY,
        NUM
    };
public:
    const Json::Value serializer()
    {
        Json::Value root;
        root["battery"] = _battery;
        return root;
    }
    bool deserializer(const std::string& message)
    {
        Json::Value root;
        if (!Json::Reader().parse(message, root))
            return false;
        return deserializer(root);
    }
    bool deserializer(const Json::Value& root)
    {
        if (root.isMember("battery") && root["battery"].isInt())
            _battery = root["battery"].asInt();
        return true;
    }
    bool modify(int type, const std::string& message)
    {
        switch (type)
        {
        case MODIFY_BATTERY :
            _battery = std::stoi(message);
            return true;
        default :
            return false;
        }
    }
    bool modify(int type, const Json::Value& root)
    {
        switch (type)
        {
        case MODIFY_BATTERY :
            if (root.isMember("battery") && root["battery"].isInt())
                _battery = root["battery"].asInt();
            return true;
        default :
            return false;
        }
    }
public:
    int _battery = 0;
};

class User_Info
{
private:
    typedef Device_Info unit_t;
    typedef User_Info self;
public:
    enum MODIFY
    {
        NONE = AccountUnit::MODIFY::NUM,
        INCREASE_MONEY,
        DECREASE_MONEY,
        NUM
    };
public:
    const Json::Value serializer()
    {
        Json::Value root;
        root["money"] = _money;
        Json::Value device_list(Json::arrayValue);
        for (auto it :  _device_list)
        {
            Json::Value device;
            device["id"] = it.first;
            device["info"] = it.second.serializer();
            device_list.append(std::move(device));
        }
        root["device_list"] = device_list;
        
        return root;
    }
    bool deserializer(const std::string& message)
    {
        Json::Value root;
        if (!Json::Reader().parse(message, root))
            return false;
        if (root.isMember("money") && root["money"].isInt())
            _money = root["money"].asInt();
        if (root.isMember("device_list") && root["device_list"].isArray())
        {
            const Json::Value& devicesArray = root["device_list"];
            for (Json::ArrayIndex i = 0; i < devicesArray.size(); ++i)
            {
                const Json::Value& device = devicesArray[i];
                bool ok = false;
                std::string id;
                if (device.isMember("id") && device["id"].isString())
                {
                    id = device["id"].asString();
                    ok = true;
                }
                unit_t unit;
                if (device.isMember("info") && device["info"].isObject())
                    ok &= unit.deserializer(device["info"]);
                if (ok)
                    _device_list[id] = unit;
            }
        }
        return true;
    }
    bool modify(int type, const std::string& message)
    {
        switch (type)
        {
        case INCREASE_MONEY :
            _money += std::stoi(message);
            return true;
        case DECREASE_MONEY :
            _money -= std::stoi(message);
            return true;
        default :
            Json::Value root;
            if (!Json::Reader().parse(message, root))
                return false;
            if (root.isMember("id") && root["id"].isString())
            {
                std::string id = root["id"].asString();
                if (_device_list.find(id) != _device_list.end())
                    return _device_list[id].modify(type, root);
                else
                {
                    unit_t unit;
                    unit.deserializer(root);
                    _device_list.insert({id, unit});
                }
                return true;
            }
            return false;
        }
    }
public:
    int _money = 0;
    std::map<std::string, unit_t> _device_list;
};
